#pragma once
#include "db.hpp"
#include "online.hpp"
#include "session.hpp"
#include "room.hpp"
#include "matcher.hpp"
#include "util.hpp"
#include <chrono>

#define WWWROOT "./wwwroot"
class gobang_server
{
public:
    gobang_server(const std::string &host, uint16_t port, const std::string &user,
        const std::string &password, const std::string &dbname, const std::string &webroot = WWWROOT
    ):_webroot(webroot),
    _table(host, port, user, password, dbname),
    _room(&_table, &_online),
    _match(&_room, &_table, &_online),
    _session(&_wsvr),
    _check_thread(std::thread(&gobang_server::checkInactiveUsers, this))
    {
        _wsvr.set_access_channels(websocketpp::log::alevel::none);
        _wsvr.init_asio();
        _wsvr.set_reuse_addr(true);
        _wsvr.set_http_handler(std::bind(&gobang_server::http_handler, this, std::placeholders::_1));
        _wsvr.set_open_handler(std::bind(&gobang_server::ws_open_handler, this, std::placeholders::_1));
        _wsvr.set_close_handler(std::bind(&gobang_server::ws_close_handler, this, std::placeholders::_1));
        _wsvr.set_message_handler(std::bind(&gobang_server::ws_msg_handler, this, std::placeholders::_1, std::placeholders::_2));
        // 在程序运行起来之前，将所有的用户的状态的设置为离线
        _table.set_all_offline();
    }
    ~gobang_server()
    {
        _check_thread.join();
    }
    void start(int port)
    {
        _wsvr.listen(port);
        _wsvr.start_accept();
        _wsvr.run();
    }
private:
    // http请求
    void http_handler(websocketpp::connection_hdl hdl)
    {
        ws_server_t::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string method = req.get_method();
        std::string uri = req.get_uri();
	    if(method == "POST" && uri == "/reg") {
	        return user_register(conn); 
	    }
	    else if(method == "POST" && uri == "/login") {
	        return user_login(conn);
	    }
	    else if(method == "GET" && uri == "/info") {
	        return user_info(conn);
	    }
	    else {
	        return static_resource(conn);
	    }
    }
    // websocket长连接建立之后的处理函数
    void ws_open_handler(websocketpp::connection_hdl hdl)
    {
        // 根据uri路径的不同，判断处于哪种长连接
        ws_server_t::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall") {
            // 游戏大厅长连接
            return ws_open_game_hall(conn);
        }
        else if(uri == "/room") {
            // 游戏房间长连接
            return ws_open_game_room(conn);
        }
    }
    // websocket长连接断开之前的处理函数
    void ws_close_handler(websocketpp::connection_hdl hdl)
    {
        // 根据uri路径的不同，判断处于哪种长连接
        ws_server_t::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall") {
            // 游戏大厅长连接
            return ws_close_game_hall(conn);
        }
        else if(uri == "/room") {
            // 游戏房间长连接
            return ws_close_game_room(conn);
        }
    }
    // websocket长连接请求处理函数
    void ws_msg_handler(websocketpp::connection_hdl hdl, ws_server_t::message_ptr msg)
    {
        // 根据uri路径的不同，判断处于哪种长连接
        ws_server_t::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall") {
            // 游戏大厅长连接
            return ws_msg_game_hall(conn, msg);
        }
        else if(uri == "/room") {
            // 游戏房间长连接
            return ws_msg_game_room(conn, msg);
        }
    }
private://http业务处理
    void static_resource(ws_server_t::connection_ptr &conn)//静态资源获取
    {
        // 1.获取客户端访问的uri资源
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        std::string realpath = _webroot;
        // 2.判断访问资源
        if(uri == "/") {// 客户端访问时没有指定访问路径，我们返回主页index.html给客户端
            realpath = realpath + uri + "index.html";
        }
        else {
            realpath = realpath + uri;
        }
        // 3.读取文件内容
        Json::Value resp;
        std::string body;
        bool ret = file_util::read(realpath, body);
        if(!ret) {// 如果文件读取失败，则返回404页面
            file_util::read("./wwwroot/404.html", body);
            conn->set_status(websocketpp::http::status_code::not_found);
            conn->set_body(body);
	        return;
        }
        conn->set_status(websocketpp::http::status_code::ok);
        conn->set_body(body);
    }
    void user_register(ws_server_t::connection_ptr &conn)
    {// 用户注册请求功能的处理
        // 1.获取请求正文
	    std::string json_string = conn->get_request_body();
	    // 2.对请求正文进行反序列化，拿到username和password
	    Json::Value register_info;
	    bool ret = json_util::deserialize(json_string, register_info);	
	    if(ret == false) {// 反序列化失败
            debug("%s", "反序列化失败");
	        return http_response(conn, false, "请求正文格式错误", websocketpp::http::status_code::bad_request);
	    }
	    // 3.进行数据库新增操作先判断用户名密码是否为空
        if(register_info["username"].isNull() || register_info["password"].isNull()) {
            debug("%s", "用户名/密码为空");
	        return http_response(conn, false, "请输入用户名/密码", websocketpp::http::status_code::bad_request);
        }
	    ret = _table.insert(register_info);
	    if(ret == false) {
            debug("%s", "用户名已经存在");
	        return http_response(conn, false, "用户名已经被占用", websocketpp::http::status_code::bad_request);
	    }
	    // 4.注册用户成功
	    return http_response(conn, true, "注册成功", websocketpp::http::status_code::ok);
    }
    void user_login(ws_server_t::connection_ptr &conn)
    {//用户登录请求功能的实现
        // 1.获取请求正文
        std::string json_str = conn->get_request_body();
        // 2.对请求正文进行反序列化，拿出username和password
        Json::Value login_info;
        bool ret = json_util::deserialize(json_str, login_info);
        if(ret == false) {
            debug("%s", "反序列化失败");
	        return http_response(conn, false, "请求正文格式错误", websocketpp::http::status_code::bad_request);
        }
        // 3.判断用户名/密码是否为空
        if(login_info["username"].isNull() || login_info["password"].isNull()) {
            debug("%s", "用户名/密码为空");
	        return http_response(conn, false, "请输入用户名/密码", websocketpp::http::status_code::bad_request);
        }
        // 3.在数据库中进行查询，判断用户名密码是否存在
        ret = _table.login(login_info);
        if(ret == false) {
            debug("%s", "用户名/密码错误");
	        return http_response(conn, false, "用户名/密码错误", websocketpp::http::status_code::bad_request);
        }
        // 4.为该用户创建一个seesion
        int uid = login_info["id"].asInt();
        std::shared_ptr<session> ssp = _session.create_session(uid, LOGIN);
        if(ssp.get() == nullptr) {
            debug("%s", "创建会话失败");
	        return http_response(conn, false, "创建会话失败", websocketpp::http::status_code::bad_request);
        }
	    // 设置session过期时间
	    _session.set_expired_time(ssp->session_id(), 30000);
        // 5.响应，并设置cookie
        std::string cookie = "SSID=" + std::to_string(ssp->session_id());
        conn->append_header("Set-Cookie", cookie);
	    return http_response(conn, true, "登录成功", websocketpp::http::status_code::ok);
    }
    void user_info(ws_server_t::connection_ptr &conn)
    {//获取用户信息请求功能
        // 1.判断请求中是否携带cookie信息
        std::string cookie_str = conn->get_request_header("Cookie");
        if(cookie_str.empty()) {
	        return http_response(conn, false, "找不到cookie信息，请重新登录", websocketpp::http::status_code::bad_request);
        }
        // 2.通过cookie中ssid找到对应的session
        std::string ssid;
        bool ret = get_cookie_value(cookie_str, "SSID", ssid);
        if(ret == false) {
	        return http_response(conn, false, "找不到ssid，请重新登录", websocketpp::http::status_code::bad_request);
        }
        std::shared_ptr<session> ssp = _session.get_session(std::stoi(ssid));
        if(ssp.get() == nullptr) {
	        return http_response(conn, false, "登录过期，请重新登录", websocketpp::http::status_code::bad_request);
        }
        // 3.从数据库取出该用户的信息
        int uid = ssp->get_user();
        Json::Value info;
        std::string body;
        ret = _table.select_by_id(uid, info); 
        if(ret == false) {
	        return http_response(conn, false, "找不到用户信息，请重新登录", websocketpp::http::status_code::bad_request);
        }
        json_util::serialize(info, body);
        conn->set_status(websocketpp::http::status_code::ok);
        conn->set_body(body);
	    conn->append_header("Content-Type", "application/json");
        // 4.刷新cookie的过期时间
        _session.set_expired_time(ssp->session_id(), 30000);
    }
private:// websocket长连接业务处理---游戏大厅
    void ws_open_game_hall(ws_server_t::connection_ptr &conn)
    {
        Json::Value err_resp;
        //1-2 获取当前用户的会话信息
        std::shared_ptr<session> ssp = get_session_by_cookie(conn);
        // 3.登录成功之后，判断该用户是否重复登录
        // 判断方法：看在 在线用户模块中 是否已经在该用户id，hall/room
        if(_online.is_in_game_hall(ssp->get_user()) || _online.is_in_game_room(ssp->get_user())) {
            err_resp["optype"] = "hall_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "重复登录";
            return websocket_response(conn, err_resp);
        }
        // 4.登录成功，并且没有重复登录，现在将该用户添加到在线用户管理句柄中
        _online.enter_game_hall(ssp->get_user(), conn);
        err_resp["optype"] = "hall_ready";
        err_resp["result"] = true;
        websocket_response(conn, err_resp);
        // 5.添加到游戏大厅后，将该用户的session信息更新为永久
        debug("进入游戏大厅，session被设为永久->%d", ssp->session_id());
        _session.set_expired_time(ssp->session_id(), SESSION_FOREVER);

        // 6.通知其所有的好友，我上线了
        // 填写自己的信息
        Json::Value up_status, info;
        up_status["optype"] = "friend_status_change";
        up_status["result"] = true;
        up_status["data"] = Json::Value(Json::arrayValue); 
        _table.select_by_id(ssp->get_user(), info);
        up_status["data"].append(info);

        // 获取所有好友id
        std::vector<int> ids;
        _table.select_friend_id_by_id(ssp->get_user(), ids);
        for(auto id : ids) {
            if(_online.is_in_game_hall(id)) {
                auto conn = _online.get_conn_from_hall(id);
                websocket_response(conn, up_status);
            }                
        }
        return;
    }
    void ws_close_game_hall(ws_server_t::connection_ptr &conn)
    {
    // 1.首先获取当前连接的会话信息
        std::shared_ptr<session> ssp = get_session_by_cookie(conn);
    // 2.将该连接对应的用户从在线用户模块中移除
         // 这里要传入user_id
        _online.exit_game_hall(ssp->get_user());
    // 3.重新设置sesion的声明周期
        debug("退出游戏大厅，session被设为临时->%d", ssp->session_id());
        _session.set_expired_time(ssp->session_id(), 30000);
    }
    void ws_msg_game_hall(ws_server_t::connection_ptr &conn, ws_server_t::message_ptr &msg)
    {
        // 1.获取当前连接的会话信息
        std::shared_ptr<session> ssp = get_session_by_cookie(conn);
        if(ssp.get() == nullptr) return;
        // 2.获取请求
        std::string req_str = msg->get_payload();
        Json::Value req_json, resp_json;
        bool ret = json_util::deserialize(req_str, req_json);
        if(ret == false) {
            resp_json["result"] = false;
            resp_json["reason"] = "请求解析失败";
            return websocket_response(conn, resp_json);
        }
        // 3.对请求进行处理
        if(!req_json["optype"].isNull() && req_json["optype"].asString() == "match_start") {
            // 开始匹配请求
            _match.add(ssp->get_user());
            resp_json["optype"] = "match_start";
            resp_json["result"] = true;
            printf("conn: %p\n", conn.get());
            return websocket_response(conn, resp_json);
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "match_stop") {
            // 停止匹配请求
            _match.del(ssp->get_user());
            resp_json["optype"] = "match_stop";
            resp_json["result"] = true;
            return websocket_response(conn, resp_json);
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "get_friend_list") {
            get_friend_list(ssp->get_user(), resp_json);
            return websocket_response(conn, resp_json);
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "add_friend") {
            // 收到添加好友请求，应该将这条消息转发给被请求方
            int dst_id = std::stoi(req_json["dst_id"].asString());
            std::string dst_username = req_json["dst_username"].asString();
            // 1.先判断该用户是否存在
            if(_table.is_exists(dst_username)) {
                // 2.判断用户在大厅中还是在游戏中，还是离线
                if(_online.is_in_game_hall(dst_id)) {
                    // 在大厅中,直接向该用户发送请求
                    resp_json = req_json;
                    resp_json["result"] = true;
                    ws_server_t::connection_ptr dst_conn = _online.get_conn_from_hall(dst_id);// 获取对方连接
                    return websocket_response(dst_conn, resp_json);
                }else if(_online.is_in_game_room(dst_id)) {
                    // 正在游戏中 -----TODO
                }
            }
            resp_json["result"] = false;
            resp_json["reason"] = "The user does not exist";
            return websocket_response(conn, resp_json);
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "add_friend_yes") {
            // 同意添加好友
            // 调用底层数据库接口，在好友表中，建立信息
            resp_json["optype"] = "add_friend_yes";
            resp_json["result"] = true;
            int dst = std::stoi(req_json["dst_id"].asString());
            int src = std::stoi(req_json["src_id"].asString());
            _table.add_friend(dst, src);
            _table.add_friend(src, dst);
            auto dst_conn = _online.get_conn_from_hall(dst);
            auto src_conn = _online.get_conn_from_hall(src);
            // 给双方都发送关系建立成功的响应
            websocket_response(dst_conn, resp_json);
            websocket_response(src_conn, resp_json);
            // 还是得给客户端发送get_friend_list
            Json::Value up1, up2;
            get_friend_list(dst, up1);
            get_friend_list(src, up2);
            websocket_response(dst_conn, up1);
            return websocket_response(src_conn, up2);
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "add_friend_no") {
            // 拒绝添加好友
            resp_json["optype"] = "add_friend_no";
            resp_json["result"] = true;
            resp_json["reason"] = "对方拒绝添加你为好友";
            int src = std::stoi(req_json["src_id"].asString());
            auto src_conn = _online.get_conn_from_hall(src);
            return websocket_response(src_conn, resp_json);
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "logout") {
            // 退出登录
            resp_json["optype"] = "logout";
            resp_json["result"] = true;
            _table.logout(ssp->get_user());
            websocket_response(conn, resp_json);
            // 除了退出登录外，还需要给他的所有所有好友发送离线通知
            // 填写自己的信息
            Json::Value up_status, info;
            up_status["optype"] = "friend_status_change";
            up_status["result"] = true;
            up_status["data"] = Json::Value(Json::arrayValue); 
            _table.select_by_id(ssp->get_user(), info);
            up_status["data"].append(info);

            // 获取所有好友id
            std::vector<int> ids;
            _table.select_friend_id_by_id(ssp->get_user(), ids);
            for(auto id : ids) {
                if(_online.is_in_game_hall(id)) {
                    auto conn = _online.get_conn_from_hall(id);
                    websocket_response(conn, up_status);
                }                
            }
            return ;
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "send_challenge") {
            // 将该邀请消息转发给dst
            resp_json = req_json;
            resp_json["result"] = true;
            int dst = std::stoi(req_json["dst_id"].asString());
            auto dst_conn = _online.get_conn_from_hall(dst);
            return websocket_response(dst_conn, resp_json);
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "challenge_response") {
            resp_json = req_json;
            resp_json["result"] = true;

            int dst = std::stoi(req_json["dst_id"].asString());
            int src = std::stoi(req_json["src_id"].asString());
            auto src_conn = _online.get_conn_from_hall(src);
            if(req_json["ans"] == true) {
                // 如果同意了，则为两个人创建房间
                _room.create_room(dst, src);
            }
            return websocket_response(src_conn, resp_json);
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString() == "heartbeat") {
            // 心跳检测
            resp_json["optype"] = "heartbeat_ack";
            resp_json["result"] = true;
            int id = std::stoi(req_json["id"].asString());
            // 更新最后活跃时间
            _table.alert_last_active(id);
            return websocket_response(conn, resp_json);
        }

        resp_json["optype"] = "unknow";
        resp_json["result"] = false;
        return websocket_response(conn, resp_json);
    }
private://websocket长连接业务处理---游戏房间
    void ws_open_game_room(ws_server_t::connection_ptr &conn)
    {
        Json::Value err_resp;
        // 1.获取用户session信息
        std::shared_ptr<session> ssp = get_session_by_cookie(conn);// 内部已经做了session为空的处理
        // 2.判断用户是否已经在游戏大厅/游戏房间中，因为如果要加入游戏房间，说明该用户已经从游戏大厅中被移除了
        // 如果在游戏房间中，就说明该用户已经在进入了房间，不需要重新进入
        if(_online.is_in_game_hall(ssp->get_user()) || _online.is_in_game_room(ssp->get_user())) {
            err_resp["optype"] = "room_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "重复登录";
            return websocket_response(conn, err_resp);
        }
        // 3.获取该用户的游戏房间信息，因为能进入游戏房间说明已经匹配成功，为两个用户创建好了房间
        std::shared_ptr<room> room_ptr = _room.get_room_by_user_id(ssp->get_user());
        if(room_ptr.get() == nullptr) {
            err_resp["optype"] = "room_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "房间信息不存在";
            return websocket_response(conn, err_resp);
        }
        // 4.将该用户添加到在线游戏房间长连接中
        _online.enter_game_room(ssp->get_user(), conn);
        // 5.设置session为永久
        debug("进入游戏房间，session被设为永久->%d", ssp->session_id());
        _session.set_expired_time(ssp->session_id(), SESSION_FOREVER);
        _room.player_enter(ssp->get_user()); // 用户进入房间开始游戏
        // 6.响应
        err_resp["optype"] = "room_ready";
        err_resp["result"] = true;
        err_resp["room_id"] = room_ptr->room_id();
        err_resp["uid"] = ssp->get_user();
        err_resp["white_id"] = room_ptr->white_id();
        err_resp["black_id"] = room_ptr->black_id();
        return websocket_response(conn, err_resp);
    }
    void ws_close_game_room(ws_server_t::connection_ptr &conn)
    {
        // 1.获取会话信息
        std::shared_ptr<session> ssp = get_session_by_cookie(conn);// 内部已经做了session为空的处理
        // 2.从在线用户房间中移除
        _online.exit_game_room(ssp->get_user());
        // 3.从游戏房间中移除
        _room.remove_room_user(ssp->get_user());
        // 4.设置session为定时销毁
        debug("退出游戏房间，session被设为临时->%d", ssp->session_id());
        _session.set_expired_time(ssp->session_id(), 30000);
        _room.player_exit(ssp->get_user()); // 用户进入房间开始游戏
    }
    void ws_msg_game_room(ws_server_t::connection_ptr &conn, ws_server_t::message_ptr &msg)
    {
        // 1.获取会话信息
        Json::Value err_resp;
        std::shared_ptr<session> ssp = get_session_by_cookie(conn);// 内部已经做了session为空的处理
        // 2.获取房间信息
        std::shared_ptr<room> room_ptr = _room.get_room_by_user_id(ssp->get_user());
        if(room_ptr.get() == nullptr) {
            err_resp["optype"] = "unknow";
            err_resp["result"] = false;
            err_resp["reason"] = "房间信息不存在";
            return websocket_response(conn, err_resp);
        }
        // 3.对请求进行反序列化
        Json::Value req_json;
        std::string payload = msg->get_payload();
        bool ret = json_util::deserialize(payload, req_json);
        if(ret == false) {
            err_resp["optype"] = "unknow";
            err_resp["result"] = false;
            err_resp["reason"] = "反序列化请求失败";
        }
        // 4.调用房间请求处理函数
        return room_ptr->handle_request(req_json);
    }
private:
    void http_response(ws_server_t::connection_ptr &conn, bool result, const std::string &reason, websocketpp::http::status_code::value code)
    {
        std::string body;
        Json::Value resp;
	    resp["result"] = result;
	    resp["reason"] = reason;
	    json_util::serialize(resp, body);
        conn->set_status(code);
        conn->set_body(body);
	    conn->append_header("Content-Type", "application/json");
    }
    bool get_cookie_value(const std::string &cookie, const std::string &key, std::string &value)
    {
        // Cookie:SSID=xxx; username=xxx; password=xxx; 
        // 1.先根据分号空格，将所有的key=value存到一个数组中
        std::vector<std::string> kv;
        string_util::split(cookie, "; ", kv);
        // 2.在这个数组中，再以=作为分隔符，找出指定的key，返回对应的value即可
        for(auto str : kv) {
            std::vector<std::string> tmp;
            string_util::split(str, "=", tmp);
            if(tmp.size() != 2) continue;
            if(tmp[0] == key) {
                value = tmp[1];
                return true;
            }
        }
        return false;
    }
    void websocket_response(ws_server_t::connection_ptr &conn, Json::Value &resp)
    {
        std::string body;
        json_util::serialize(resp, body);
        conn->send(body);
    }
    // 通过cookie获取会话信息
    std::shared_ptr<session> get_session_by_cookie(ws_server_t::connection_ptr &conn)
    {
    // 1.首先获取当前连接的会话信息
        // 1.判断请求中是否携带cookie信息
        Json::Value err_resp;
        std::string cookie_str = conn->get_request_header("Cookie");
        if(cookie_str.empty()) {
            err_resp["optype"] = "hall_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "找不到cookie信息，请重新登录";
            websocket_response(conn, err_resp);
            return std::shared_ptr<session>();
        }
        // 2.通过cookie中ssid找到对应的session
        std::string ssid;
        bool ret = get_cookie_value(cookie_str, "SSID", ssid);
        if(ret == false) {
            err_resp["optype"] = "hall_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "找不到ssid信息，请重新登录";
            websocket_response(conn, err_resp);
            return std::shared_ptr<session>();
        }
        std::shared_ptr<session> ssp = _session.get_session(std::stoi(ssid));
        if(ssp.get() == nullptr) {
            err_resp["optype"] = "hall_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "登录过期，请重新登录";
            websocket_response(conn, err_resp);
            return std::shared_ptr<session>();
        }
        return ssp;
    }

    void get_friend_list(int uid, Json::Value &resp_json)
    {
        // 请求好友列表
        resp_json["optype"] = "get_friend_list";
        resp_json["result"] = true;
        resp_json["data"] = Json::Value(Json::arrayValue); 

        // 获取所有好友id
        std::vector<int> ids;
        _table.select_friend_id_by_id(uid, ids);

        // 获取所有好友id的具体信息
        Json::Value info;
        for(auto id : ids) {
            _table.select_by_id(id, info);
            resp_json["data"].append(info);
        }
    }
private:
    void checkInactiveUsers()
    {
        while(true) {
            // 每10秒检查一次
            std::this_thread::sleep_for(std::chrono::seconds(10));

            std::mutex _mutex;
            std::unique_lock<std::mutex> lock(_mutex);
            
            // 获取当前时间
            auto now = std::chrono::system_clock::now();
            // 遍历所有的status为在线的用户，判断其last_active与当前时间是否大于60s
            // 如果大于60s则表示该用户已经连接超时，此时将该用户的状态改为离线
            // 并且通知他的所有好友
            // 现在的问题在于：怎么遍历所有的大厅用户
            std::vector<int> hall_usrid = _online.get_hall_usrid();
            for(auto id : hall_usrid) {
                if(_table.online(id)) {
                    // 如果在线，则进行超时判断
                    std::string time = _table.last_active(id);
                    auto last = stringToTimePoint(time);
                    auto inactiveTime = std::chrono::duration_cast<std::chrono::seconds>(now - last/*id的last_active*/).count();
                    if(inactiveTime > 60) {
                        _table.logout(id); // 状态改为离线
                        // 通知所有好友
                        // 填写自己的信息
                        Json::Value up_status, info;
                        up_status["optype"] = "friend_status_change";
                        up_status["result"] = true;
                        up_status["data"] = Json::Value(Json::arrayValue); 
                        _table.select_by_id(id, info);
                        up_status["data"].append(info);

                        // 获取所有好友id
                        std::vector<int> ids;
                        _table.select_friend_id_by_id(id, ids);
                        for(auto id : ids) {
                            if(_online.is_in_game_hall(id)) {
                                auto conn = _online.get_conn_from_hall(id);
                                websocket_response(conn, up_status);
                            }                
                        }
                    }
                }
            }
        }
    }

    // 将时间戳字符串转换为system_clock::time_point
    std::chrono::time_point<std::chrono::system_clock> stringToTimePoint(const std::string& timestampStr, 
                                                                             const std::string& format = "%Y-%m-%d %H:%M:%S") {
        // 解析字符串到tm结构
        std::tm tm = {};
        std::istringstream ss(timestampStr);
        ss >> std::get_time(&tm, format.c_str());
                                                                            
        if (ss.fail()) {
            throw std::invalid_argument("Failed to parse timestamp: " + timestampStr);
        }

        // 将tm转换为time_t
        std::time_t time = std::mktime(&tm);

        // 转换为system_clock::time_point
        return std::chrono::system_clock::from_time_t(time);
    }
private:
    std::string _webroot; // web资源根目录
    ws_server_t _wsvr; // websocket服务器
    session_manager _session; //session句柄
    User_Info_Table _table; // 数据库句柄
    online_user_manager _online; // 在线用户句柄
    room_manager _room; //房间管理句柄
    matcher _match; // 匹配句柄
    std::thread _check_thread;
};
